package com.logic.modular.game.core.service;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.comment.dubbo.service.PlayerMatchService;
import com.comment.exception.ServiceException;
import com.logic.comment.util.RedisUtils;
import com.logic.config.netty.web.service.MessageService;
import com.logic.modular.game.battle.info.base.PvpPlayerInfo;
import com.logic.modular.game.battle.info.base.PvpPlayerInfoServiceImpl;
import com.logic.modular.game.battle.process.PlayerPvpServiceImpl;
import com.logic.modular.game.core.domain.MatchPool;
import com.logic.modular.game.core.mapper.PvpMatchMapper;

import com.logic.modular.game.player.bag.service.BagItemServiceImpl;
import com.comment.pojo.domain.logic.domain.PvpRanks;
import com.logic.modular.game.player.info.service.PlayerStatusServiceImpl;
import com.logic.modular.game.PvpCity;
import com.comment.pojo.domain.logic.domain.PlayerStatus;

import com.logic.modular.game.player.match.service.PvpCityServiceImpl;
import com.logic.modular.game.player.match.service.PvpNpcRanksServiceImpl;
import com.logic.modular.game.player.match.service.PvpRanksServiceImpl;
import com.comment.pojo.domain.logic.core.CoreConfig;
import com.logic.modular.gameCore.config.service.impl.CoreConfigServiceImpl;
import com.comment.pojo.domain.logic.core.CoreCity;
import com.logic.modular.gameCore.map.service.CoreCityServiceImpl;
import lombok.extern.slf4j.Slf4j;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.comment.pojo.domain.logic.core.PvpMatch;
import org.apache.dubbo.config.annotation.DubboService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.annotation.Resource;

import static com.logic.modular.game.core.service.MatchTaskServiceImpl.SURVIVAL_MATCH_IDS;
import static com.logic.modular.game.core.service.MatchTaskServiceImpl.survivalMatchIds;

/**
 * 比赛服务
 */
@Slf4j
@Service
@DubboService(interfaceClass = PlayerMatchService.class)
public class MatchServiceImpl extends ServiceImpl<PvpMatchMapper, PvpMatch>
        implements PlayerMatchService {
    @Resource
    private PvpCityServiceImpl pvpCityService;
    @Resource
    private PvpCityServiceImpl pvpCityServiceImpl;
    @Resource
    private PvpPlayerInfoServiceImpl playerInfoService;
    @Resource
    private PlayerStatusServiceImpl playerStatusService;
    @Resource
    private PvpNpcRanksServiceImpl pvpNpcRanksServiceImpl;
    @Resource
    private MatchEndServiceImpl pvpMatchEndServiceImpl;
    @Resource
    private PvpPlayerInfoServiceImpl pvpPlayerInfoServiceImpl;
    @Resource
    private BagItemServiceImpl bagItemService;
    @Resource
    private BagItemServiceImpl bagItemServiceImpl;
    @Resource
    private RedisUtils redisUtils;
    @Resource
    private PlayerPvpServiceImpl playerPvpService;
    @Resource
    private PvpRanksServiceImpl pvpRanksServiceImpl;
    @Resource
    private PlayerStatusServiceImpl playerStatusServiceImpl;
    @Resource
    private com.logic.modular.game.robot.match.RobotMatchActionServiceImpl robotMatchActionServiceImpl;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private PvpMatchMapper pvpMatchMapper;


    /**
     * 人机对局
     */
    @Transactional
    public PvpMatch manMachineMatch(
            Long playerId,
            Long mapId,
            Integer startThreshold
    ) {
        String token = "stopTheGame";
        if (redisUtils.hasKey(token)) {
            throw new ServiceException("禁赛中");
        }
        Long currentMatchId = playerStatusService.currentMatchId(playerId);
        if (currentMatchId != 0) {
            return getById(currentMatchId);
        }
        /// //////////////////////////////////////////////////////////////// 获取 realPersonMatchQuick
        PvpMatch select = new PvpMatch();
        select.setPvpMapId(mapId);
        select.setType(2);
        select.setStatus(0);
        select.setStartThreshold(startThreshold);
        LambdaQueryWrapper<PvpMatch> freeManMachineMatchWrapper = new LambdaQueryWrapper<>(select);

        PvpMatch pvpMatch = select;
        pvpMatch.setWinnerIds(new HashSet<>());
        pvpMatch.setPlayerCount(new HashSet<>());
        pvpMatch.setPlayerIds(new HashSet<>());
        if (pvpMatch.insert()) {
            pvpMatch = list(freeManMachineMatchWrapper).stream().findFirst().orElse(null);
        }
        if (pvpMatch == null) {
            throw new ServiceException("请重试");
        }
        // 上锁防止竞争
        String manMachineMatchLockKey = "manMachineMatch:" + pvpMatch.getId();
        RLock lock = redissonClient.getLock(manMachineMatchLockKey);
        try {
            // 尝试获取锁，最多等待10秒，锁自动释放时间为30秒
            boolean isLocked = lock.tryLock(10, 30, TimeUnit.SECONDS);
            if (!isLocked) {
                // 这里等待一下尝试方法
                Thread.sleep(1000);
                return manMachineMatch(playerId, mapId, startThreshold);
            }

            // 人机加入
            pvpMatch = robotMatchActionServiceImpl.humanMachineJoiningMatchingBehavior(pvpMatch);

            Set<Long> playerIds = pvpMatch.getPlayerIds();
            if (playerIds.contains(playerId)) {
                return pvpMatch;
            }
            pvpMatch.playerCount.add(playerId);
            pvpMatch.playerIds.add(playerId);
        } catch (InterruptedException e) {
            log.error("Interrupted while trying to acquire lock", e);
            Thread.currentThread().interrupt();
            throw new RuntimeException(e);
        } finally {
            // 释放锁
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        boolean playerIdsCheckOk = pvpMatch.playerIds.size() == pvpMatch.getStartThreshold();
        if (playerIdsCheckOk) {
            playerStatusService.updateMatch(playerId, pvpMatch.getId());
            return startPvp(pvpMatch);
        }
        return pvpMatch;
    }

    /**
     * 匹配对局
     */
    @Transactional
    public void realPersonMatchQuick(
            Long playerId,
            Long mapId,
            Integer startThreshold
    ) {
        String token = "stopTheGame";
        if (redisUtils.hasKey(token)) {
            throw new ServiceException("禁赛中");
        }
        Long currentMatchId = playerStatusService.currentMatchId(playerId);
        if (currentMatchId > 0) {
            return;
        }
        // 上锁防止竞争
        String matchLockKey = "realPersonMatchQuick-" + String.format(MatchPool.MATCH_POOL_KEY2, mapId, 1, startThreshold);
        RLock lock = redissonClient.getLock(matchLockKey);
        Set<Long> playerIds = Collections.emptySet();
        try {
            boolean isLocked = lock.tryLock(30, 60, TimeUnit.SECONDS);
            if (!isLocked) {
                // 这里等待一下尝试方法
                Thread.sleep(1000);
                realPersonMatchQuick(playerId, mapId, startThreshold);
                return;
            }
            // 加入匹配池
            MatchPool.joinMatchPool(playerId, mapId, 1, startThreshold);
            playerStatusService.updateMatch(playerId, -1L);
            // 尝试获取可开始的玩家
            playerIds = MatchPool.tryStarMatch(mapId, 1, startThreshold);
            if (playerIds == null) {
                return;
            }
        } catch (InterruptedException e) {
            log.error("尝试获取匹配对局锁时中断", e);
            Thread.currentThread().interrupt();
        } finally {
            // 释放锁
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }

        PvpMatch match = new PvpMatch();
        match.setStartTime(new Date());
        match.setPvpMapId(mapId);
        match.setType(1);
        match.setPlayerIds(playerIds);
        match.setPlayerCount(playerIds);
        match.setStatus(0);
        pvpMatchMapper.insert(match);
        match = startPvp(match);
        for (Long matchUserId : match.playerCount) {
            playerStatusServiceImpl.updateMatch(matchUserId, match.getId());
        }
    }

    /**
     * 开始对局
     */
    @Transactional
    public PvpMatch startPvp(PvpMatch pvpMatch) {
        pvpMatch.setStatus(1);
        pvpMatch.setStartTime(new Date());
        updateById(pvpMatch);

        // 城池初始化
        List<PvpCity> initCities = pvpCityService.init(pvpMatch);
        // 获取可分配主城
        List<PvpCity> cities = initCities.stream()
                .filter(item -> {
                    CoreCity coreCity = CoreCityServiceImpl.getCache(item.getCoreCityId());
                    return coreCity.getIsBirthCity() == 1;
                }).collect(Collectors.toList());
        // ///////////获取玩家
        for (Long uid : pvpMatch.getPlayerIds()) {
            // 初始化用户背包 copper
            initMatchCopper(uid);

            // 分配城池
            PvpCity city = cities.remove(new Random().nextInt(cities.size()));
            initCities.remove(city);
            pvpCityServiceImpl.initPvpPlayerCity(uid, city);
            playerInfoService.init(city);

            for (PvpRanks pvpRanks : pvpRanksServiceImpl.obtainPvpRanKs(uid)) {
                pvpRanks.clear();
                pvpRanksServiceImpl.updateById(pvpRanks);
            }
        }
        initCities.forEach(pvpCity -> pvpNpcRanksServiceImpl.init(pvpCity.getMatchId(), pvpCity.getCoreCityId()));
        joinMatchHeart(pvpMatch.getId());

        Integer type = pvpMatch.getType();
        if (type.equals(2)) {
            robotMatchActionServiceImpl.teamsAction1(pvpMatch);
            com.logic.modular.game.robot.po.RobotInfo.saveNeedRobotTeamsAttackMatchId(pvpMatch.getId());
        }

        return pvpMatch;
    }

    @Override
    public boolean updateById(PvpMatch entity) {
        if (super.updateById(entity)) {
            MessageService.sendMessageToMatch(entity.getPlayerIds(), MessageService.updateJsonObject("pvpMatch", entity));
        }
        return true;
    }


    private void initMatchCopper(Long uid) {
        CoreConfig initialCurrencyConfig = CoreConfigServiceImpl.getCache(7L);
        bagItemService.obtainBagItem(uid, 4L, initialCurrencyConfig.getValue1());
        bagItemService.obtainBagItem(uid, 5L, new BigDecimal(initialCurrencyConfig.getValue2()));
    }


    /**
     * 取消匹配
     */
    public PvpMatch cancelMatch(Long playerId) {
        boolean b = MatchPool.leftMatchPool(playerId);
        if (!b) {
            return null;
        }

        PlayerStatus playerMatch = playerStatusService.get(playerId);
        if (playerMatch.getCurrentMatchId() == null) {
            return null;
        }

        playerMatch.setCurrentMatchId(0L);
        playerStatusService.updateById(playerMatch);
        return null;
    }


    /**
     * 加入对局心跳
     *
     * @param matchId 对局id
     */
    private void joinMatchHeart(Long matchId) {
        Set<Long> result = survivalMatchIds.get(SURVIVAL_MATCH_IDS);
        if (result == null) {
            result = new HashSet<>();
        }
        result.add(matchId);
        survivalMatchIds.put(SURVIVAL_MATCH_IDS, result);
    }

    /**
     * 退出对局心跳
     *
     * @param matchId 对局id
     */
    public void exitMatchHeart(Long matchId) {
        Set<Long> result = survivalMatchIds.get(SURVIVAL_MATCH_IDS);
        if (result == null) {
            log.info("当前没有对局");
            return;
        }
        result.remove(matchId);
        survivalMatchIds.put(SURVIVAL_MATCH_IDS, result);
    }

    /**
     * 重置 玩家参与的对局
     */
    public void resetPvp(Long matchId, Long playerId) {
        PvpPlayerInfo pvpPlayerInfo = pvpPlayerInfoServiceImpl.get(matchId, playerId);
        PvpMatch pvpMatch = getById(matchId);
        if (pvpPlayerInfo.getIsRuler() == 1) {
            pvpMatch.addWinnerIds(playerId);
        }
        pvpMatch.removePlayerId(playerId);
        updateById(pvpMatch);
    }

    /**
     * 获取个人比赛
     */
    @Override
    public PvpMatch obtainOwnerMatch(Long playerId) {
        PlayerStatus playerStatus = playerStatusServiceImpl.get(playerId);
        Long currentMatchId = playerStatus.getCurrentMatchId();
        return getById(currentMatchId);
    }

    public void testGame(Long playerId, Long initCityId) {
        Long matchId = -playerId;

        PlayerStatus playerStatus = playerStatusServiceImpl.get(playerId);
        playerStatus.setCurrentMatchId(matchId);
        PvpMatch pvpMatch = new PvpMatch();
        pvpMatch.setId(matchId);
        // 初始化城池
        pvpCityService.init(pvpMatch);
        PvpCity pvpCity = pvpCityService.getPvpCity(initCityId, matchId);
        pvpCity.setOwnerUid(playerId);
        pvpCityService.update(pvpCity);
        // 分配无限粮食
        bagItemService.obtainBagItem(playerId, 4L, new BigDecimal(999999999999999999L));
        bagItemService.obtainBagItem(playerId, 5L, new BigDecimal(999999999999999999L));
    }
}